/*
 * $Id: str.c,v 1.7 2006-05-04 15:59:43 bacon Exp $
 */

#include <xp/bas/str.h>
#include <xp/bas/string.h>
#include <xp/bas/memory.h>

/* dynamic string functions */
xp_str_t* xp_str_open (xp_str_t* str, xp_size_t capa)
{
	if (str == XP_NULL) {
		str = (xp_str_t*)xp_malloc (sizeof(xp_str_t));
		if (str == XP_NULL) return XP_NULL;
		str->__dynamic = xp_true;
	}
	else str->__dynamic = xp_false;

	str->buf = (xp_char_t*)xp_malloc (
		xp_sizeof(xp_char_t) * (capa + 1));
	if (str->buf == XP_NULL) {
		if (str->__dynamic) xp_free (str);
		return XP_NULL;
	}

	str->size      = 0;
	str->capa  = capa;
	str->buf[0] = XP_CHAR('\0');

	return str;
}

void xp_str_close (xp_str_t* str)
{
	xp_free (str->buf);
	if (str->__dynamic) xp_free (str);
}

void xp_str_forfeit (xp_str_t* str)
{
	if (str->__dynamic) xp_free (str);
}

xp_char_t* xp_str_yield (xp_str_t* str, xp_size_t capa)
{
	xp_char_t* old_buf, * new_buf;

	new_buf = (xp_char_t*)
		xp_malloc(xp_sizeof(xp_char_t) * (capa + 1));
	if (new_buf == XP_NULL) return XP_NULL;

	old_buf = str->buf;
	str->buf    = new_buf;
	str->size      = 0;
	str->capa  = capa;
	str->buf[0] = XP_CHAR('\0');

	return old_buf;
}

xp_char_t* xp_str_cield (xp_str_t* str)
{
	xp_char_t* b = str->buf;
	if (str->__dynamic) xp_free (str);
	return b;
}

xp_size_t xp_str_len (xp_str_t* str)
{
	return str->size;
}

xp_size_t xp_str_size (xp_str_t* str)
{
	return str->size + 1;
}

xp_size_t xp_str_capa (xp_str_t* str)
{
	return str->capa;
}

const xp_char_t* xp_str_buf (xp_str_t* str)
{
	return str->buf;
}

#if 0
int xp_str_putc (xp_str_t* str, xp_size_t index, xp_char_t c)
{
	/* TODO: implement this. */
}
#endif

xp_size_t xp_str_cpy (xp_str_t* str, const xp_char_t* s)
{
	/* TODO: improve it */
	return xp_str_ncpy (str, s, xp_strlen(s));
}

xp_size_t xp_str_ncpy (xp_str_t* str, const xp_char_t* s, xp_size_t len)
{
	xp_char_t* buf;

	if (len > str->capa) {
		buf = (xp_char_t*)xp_malloc (xp_sizeof(xp_char_t) * (len + 1));
		if (buf == XP_NULL) return (xp_size_t)-1;

		xp_free (str->buf);
		str->capa = len;
		str->buf = buf;
	}

	str->size = xp_strncpy (str->buf, s, len);
	str->buf[str->size] = XP_CHAR('\0');
	return str->size;
}

xp_size_t xp_str_cpyn (xp_str_t* str, const xp_char_t* s, xp_size_t n)
{
	xp_char_t* buf;

	if (n > str->capa) {
		buf = (xp_char_t*)xp_malloc (xp_sizeof(xp_char_t) * (n + 1));
		if (buf == XP_NULL) return (xp_size_t)-1;

		xp_free (str->buf);
		str->capa = n;
		str->buf = buf;
	}

	str->size = xp_strcpyn (str->buf, s, n);	
	str->buf[str->size] = XP_CHAR('\0');
	return str->size;
}

xp_size_t xp_str_ccpy (xp_str_t* str, xp_char_t c)
{
	return xp_str_ncpy (str, &c, 1);
}

xp_size_t xp_str_cat (xp_str_t* str, const xp_char_t* s)
{
	/* TODO: improve it */
	return xp_str_ncat (str, s, xp_strlen(s));
}

xp_size_t xp_str_ncat (xp_str_t* str, const xp_char_t* s, xp_size_t len)
{
	xp_char_t* buf;
	xp_size_t capa;

	if (len > str->capa - str->size) {
		capa = str->size + len;

		/* double the capa if necessary for concatenation */
		if (capa < str->capa * 2) capa = str->capa * 2;

		buf = (xp_char_t*)xp_realloc (
			str->buf, xp_sizeof(xp_char_t) * (capa + 1));
		if (buf == XP_NULL) return (xp_size_t)-1;

		str->capa = capa;
		str->buf = buf;
	}

	str->size += xp_strncpy (&str->buf[str->size], s, len);
	str->buf[str->size] = XP_CHAR('\0');
	return str->size;
}

xp_size_t xp_str_catn (xp_str_t* str, const xp_char_t* s, xp_size_t n)
{
	xp_char_t* buf;
	xp_size_t capa;

	if (n > str->capa - str->size) {
		capa = str->size + n;

		/* double the capa if necessary for concatenation */
		if (capa < str->capa * 2) capa = str->capa * 2;

		buf = (xp_char_t*)xp_realloc (
			str->buf, xp_sizeof(xp_char_t) * (capa + 1));
		if (buf == XP_NULL) return (xp_size_t)-1;

		str->capa = capa;
		str->buf = buf;
	}

	str->size += xp_strcpyn (&str->buf[str->size], s, n);
	str->buf[str->size] = XP_CHAR('\0');
	return str->size;
}

xp_size_t xp_str_ccat (xp_str_t* str, xp_char_t c)
{
	return xp_str_ncat (str, &c, 1);
}

xp_size_t xp_str_nccat (xp_str_t* str, xp_char_t c, xp_size_t len)
{
	while (len > 0)
	{
		if (xp_str_ncat (str, &c, 1) == (xp_size_t)-1) 
		{
			return (xp_size_t)-1;
		}

		len--;
	}
	return str->size;
}

xp_size_t xp_str_del (xp_str_t* str, xp_size_t pos, xp_size_t n)
{
	str->size = xp_strxdel (str->buf, str->size, pos, n);
	str->buf[str->size] = XP_CHAR('\0');
	return str->size;
}

void xp_str_clear (xp_str_t* str)
{
	str->size = 0;
	str->buf[0] = XP_CHAR('\0');
}

